/*
    异步工作器
*/

#ifndef __M_LOOPER_H__
#define __M_LOOPER_H__
#include "buffer.hpp"
#include <mutex>
#include <condition_variable>
#include <thread>
#include <functional>
#include <atomic>

namespace Hxy_Log
{
    using Functor = std::function<void(Buffer&)>;
    enum class LoopType
    {
        ASYNC_SAFE,
        ASYNC_UNSAFE
    };
    class AsyncLooper
    {
        public:
            using ptr = std::shared_ptr<AsyncLooper>;
            AsyncLooper(const Functor& cb,LoopType ls)
                :_stop(false),
                 _lpsafe(ls),
                 _thread(std::thread(&AsyncLooper::_ThreadEntry,this)),
                 _callback(cb){}
            ~AsyncLooper(){ stop(); }
            void push(const char* data,size_t len)
            {
                //1.非安全模式 -- 无限扩容，2.安全工作模式 -- 生产区中数据满了就阻塞
                //进行放数据需要进行加锁
                std::unique_lock<std::mutex> mutex(_mtx);
                //需要判断输入的长度够不够存放   --  当条件判断成false的时候进行等待
                if(_lpsafe == LoopType::ASYNC_SAFE)
                    _cond_pro.wait(mutex,[&](){return _product.writeableSize()>=len;});
                //走到这能够进行压入数据
                _product.push(data,len);
                //成功放入数据后，需要进行唤醒一个消费者进行拿数据
                _cond_con.notify_one();
            }
            void stop()
            {
                //对工作线程进行停止
                _stop = true;   //保证停止的操作是安全的 -- 当多个线程进行听着和输入的时候
                //停止生产后 生产者缓存区有数据，唤醒消费者进行取数据
                _cond_con.notify_all();
                //唤醒消费者后会调用对应消费者线程 -- 需要进行等待消费者线程进行回收
                _thread.join();
            }
        private:
            void _ThreadEntry() //线程入口函数
            {
                while(1)
                {   
                    //限制互斥锁的生命周期，避免串行化处理数据
                    {
                    //线程需要保持互斥性
                    std::unique_lock<std::mutex> mutex(_mtx);
                    //消费者线程进行等待，当日志器工作标志为false时进行等待，此时生产者正在生产资源
                    //当生产者缓存区为空的时候进行等待，为空没有可以拿取的数据 -- 条件变量为true的时候会进行执行
                    if(_stop && _product.empty()) break;    //进行死循环读取，当生产缓存区为空且工作器停止工作后退出线程
                    if(_lpsafe == LoopType::ASYNC_SAFE)
                        _cond_con.wait(mutex,[&](){return _stop || !_product.empty();});//消费者线程进行等待
                    //进行交换两个缓存区的数据
                    _consumer.swap(_product);
                    //唤醒生产者线程进行生产
                    _cond_pro.notify_all();
                    }
                    //调用对应的回调函数进行处理数据
                    _callback(_consumer);                    
                    //重置消费缓存区 -- 与生产缓存区互换了
                    _consumer.reset();
                }
            }
        private:
            Functor _callback; //回调函数 -- 具体对缓存区数据进行处理，由异步工作器使用者传入
        private:
            Hxy_Log::LoopType _lpsafe; //控制当前工作器是否使用安全或非安全状态
            std::atomic<bool> _stop;//工作器停止标识
            Buffer _product;//写入缓存区
            Buffer _consumer;//读取缓存区
            std::mutex _mtx;//互斥锁 -- 保证线程互斥
            std::condition_variable _cond_pro;//缓存区为满的条件变量 -- 保证线程同步
            std::condition_variable _cond_con;//缓存区为空的条件变量 -- 保证线程同步
            std::thread _thread;//异步工作器的工作线程
    };
}

#endif